extends layout

append style
  link(rel="stylesheet", href="/docs/css/inlinecpc.css")
  script(type="text/javascript" src="/docs/js/native.js")

block content
  :markdown
    ## Subdocuments

    <script>
      _native.init("CK7DT53U",{
        targetClass: 'native-inline'
      });
    </script>

    <div class="native-inline">
      <a href="#native_link#"><span class="sponsor">Sponsor</span> #native_company# — #native_desc#</a>
    </div>

    Subdocuments are documents embedded in other documents. In Mongoose, this
    means you can nest schemas in other schemas. Mongoose has two
    distinct notions of subdocuments: arrays of subdocuments and single nested
    subdocuments.
    ```javascript
    var childSchema = new Schema({ name: 'string' });

    var parentSchema = new Schema({
      // Array of subdocuments
      children: [childSchema],
      // Single nested subdocuments. Caveat: single nested subdocs only work
      // in mongoose >= 4.2.0
      child: childSchema
    });
    ```
    Aside from code reuse, one important reason to use subdocuments is to create
    a path where there would otherwise not be one to allow for validation over
    a group of fields (e.g. dateRange.fromDate <= dateRange.toDate).

  :markdown
    <ul class="toc">
      <li><a href="#what-is-a-subdocument-">What is a Subdocument?</a></li>
      <li><a href="#finding-a-subdocument">Finding a Subdocument</a></li>
      <li><a href="#adding-subdocs-to-arrays">Adding Subdocs to Arrays</a></li>
      <li><a href="#removing-subdocs">Removing Subdocs</a></li>
      <li><a href="#subdoc-parents">Parents of Subdocs</a></li>
      <li><a href="#altsyntaxarrays">Alternate declaration syntax for arrays</a></li>
      <li><a href="#altsyntaxsingle">Alternate declaration syntax for single subdocuments</a></li>
    </ul>

    ### What is a Subdocument?

    Subdocuments are similar to normal documents. Nested schemas can have
    [middleware](./middleware.html), [custom validation logic](./validation.html),
    virtuals, and any other feature top-level schemas can use. The major
    difference is that subdocuments are
    not saved individually, they are saved whenever their top-level parent
    document is saved.
    ```javascript
    var Parent = mongoose.model('Parent', parentSchema);
    var parent = new Parent({ children: [{ name: 'Matt' }, { name: 'Sarah' }] })
    parent.children[0].name = 'Matthew';

    // `parent.children[0].save()` is a no-op, it triggers middleware but
    // does **not** actually save the subdocument. You need to save the parent
    // doc.
    parent.save(callback);
    ```
  :markdown
    Subdocuments have `save` and `validate` [middleware](./middleware.html)
    just like top-level documents. Calling `save()` on the parent document triggers
    the `save()` middleware for all its subdocuments, and the same for `validate()`
    middleware.

    ```javascript
    childSchema.pre('save', function (next) {
      if ('invalid' == this.name) {
        return next(new Error('#sadpanda'));
      }
      next();
    });

    var parent = new Parent({ children: [{ name: 'invalid' }] });
    parent.save(function (err) {
      console.log(err.message) // #sadpanda
    });
    ```
  :markdown
    Subdocuments' `pre('save')` and `pre('validate')` middleware execute
    **before** the top-level document's `pre('save')` but **after** the
    top-level document's `pre('validate')` middleware. This is because validating
    before `save()` is actually a piece of built-in middleware.

    ```javascript
    // Below code will print out 1-4 in order
    var childSchema = new mongoose.Schema({ name: 'string' });

    childSchema.pre('validate', function(next) {
      console.log('2');
      next();
    });

    childSchema.pre('save', function(next) {
      console.log('3');
      next();
    });

    var parentSchema = new mongoose.Schema({
      child: childSchema,
        });

    parentSchema.pre('validate', function(next) {
      console.log('1');
      next();
    });

    parentSchema.pre('save', function(next) {
      console.log('4');
      next();
    });
    ```

  h3#finding-a-subdocument Finding a Subdocument
  :markdown
    Each subdocument has an `_id` by default. Mongoose document arrays have a
    special [id](./api.html#types_documentarray_MongooseDocumentArray-id) method
    for searching a document array to find a document with a given `_id`.
    ```javascript
    var doc = parent.children.id(_id);
    ```
  h3#adding-subdocs-to-arrays Adding Subdocs to Arrays
  :markdown
    MongooseArray methods such as
    [push](./api.html#mongoosearray_MongooseArray-push),
    [unshift](./api.html#mongoosearray_MongooseArray-unshift),
    [addToSet](./api.html#mongoosearray_MongooseArray-addToSet),
    and others cast arguments to their proper types transparently:
    ```javascript
    var Parent = mongoose.model('Parent');
    var parent = new Parent;

    // create a comment
    parent.children.push({ name: 'Liesl' });
    var subdoc = parent.children[0];
    console.log(subdoc) // { _id: '501d86090d371bab2c0341c5', name: 'Liesl' }
    subdoc.isNew; // true

    parent.save(function (err) {
      if (err) return handleError(err)
      console.log('Success!');
    });
    ```
  :markdown
    Subdocs may also be created without adding them to the array by using the
    [create](./api.html#types_documentarray_MongooseDocumentArray.create)
    method of MongooseArrays.
    ```javascript
    var newdoc = parent.children.create({ name: 'Aaron' });
    ```
  h3#removing-subdocs Removing Subdocs
  :markdown
    Each subdocument has it's own
    [remove](./api.html#types_embedded_EmbeddedDocument-remove) method. For
    an array subdocument, this is equivalent to calling `.pull()` on the
    subdocument. For a single nested subdocument, `remove()` is equivalent
    to setting the subdocument to `null`.
    ```javascript
    // Equivalent to `parent.children.pull(_id)`
    parent.children.id(_id).remove();
    // Equivalent to `parent.child = null`
    parent.child.remove();
    parent.save(function (err) {
      if (err) return handleError(err);
      console.log('the subdocs were removed');
    });
    ```

  h3#subdoc-parents Parents of Subdocs
  :markdown
    Sometimes, you need to get the parent of a subdoc. You can access the
    parent using the `parent()` function.

    ```javascript
    const schema = new Schema({
      docArr: [{ name: String }],
      singleNested: new Schema({ name: String })
    });
    const Model = mongoose.model('Test', schema);

    const doc = new Model({
      docArr: [{ name: 'foo' }],
      singleNested: { name: 'bar' }
    });

    doc.singleNested.parent() === doc; // true
    doc.docArr[0].parent() === doc; // true
    ```

    If you have a deeply nested subdoc, you can access the top-level document
    using the `ownerDocument()` function.

    ```javascript
    const schema = new Schema({
      level1: new Schema({
        level2: new Schema({
          test: String
        })
      })
    });
    const Model = mongoose.model('Test', schema);

    const doc = new Model({ level1: { level2: 'test' } });

    doc.level1.level2.parent() === doc; // false
    doc.level1.level2.parent() === doc.level1; // true
    doc.level1.level2.ownerDocument() === doc; // true
    ```

  h4#altsyntaxarrays Alternate declaration syntax for arrays
  :markdown
    If you create a schema with an array of objects, mongoose will automatically
    convert the object to a schema for you:
    ```javascript
    var parentSchema = new Schema({
      children: [{ name: 'string' }]
    });
    // Equivalent
    var parentSchema = new Schema({
      children: [new Schema({ name: 'string' })]
    });
    ```

  h4#altsyntaxsingle Alternate declaration syntax for single subdocuments
  :markdown
    Similarly, single subdocuments also have a shorthand whereby you can omit
    wrapping the schema with an instance of Schema.  However, for historical
    reasons, this alternate declaration must be enabled via an option (either
    on the parent schema instantiation or on the mongoose instance).
    ```javascript
    var parentSchema = new Schema({
      child: { type: { name: 'string' } }
    }, { typePojoToMixed: false });
    // Equivalent
    var parentSchema = new Schema({
      child: new Schema({ name: 'string' })
    });
    // Not equivalent!  Careful - a Mixed path is created instead!
    var parentSchema = new Schema({
      child: { type: { name: 'string' } }
    });
    ```
  h3#next Next Up
  :markdown
    Now that we've covered Subdocuments, let's take a look at
    [querying](./queries.html).
